package com.xiaomaoguai.fcp.hsf.utils;

import org.apache.http.HttpException;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.impl.io.DefaultHttpResponseParserFactory;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @fileName: HttpClientUtils.java
 * @author: WeiHui
 * @date: 2018/11/20 20:58
 * @version: v1.0.0
 * @since JDK 1.8
 */
public final class HttpClientUtils {

	private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientUtils.class);

	private static CloseableHttpClient httpClient = null;

	/**
	 * 私有构造方法
	 */
	private HttpClientUtils() {
	}

	public static synchronized CloseableHttpClient getHttpClient() {
		if (httpClient == null) {
			//注册访问协议相关的socket工厂
			Registry<ConnectionSocketFactory> socketFactoryRegistry =
					RegistryBuilder.<ConnectionSocketFactory>create()
							.register("http", PlainConnectionSocketFactory.INSTANCE)
							.register("https", SSLConnectionSocketFactory.getSystemSocketFactory())
							.build();
			//HttpConnection 工厂 :配置写请求/解析响应处理器
			HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connectionFactory = new ManagedHttpClientConnectionFactory(DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE);
			//DNS 解析器
			SystemDefaultDnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;

			PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry, connectionFactory, dnsResolver);

			//默认为socket配置
			SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
			manager.setDefaultSocketConfig(socketConfig);
			//设置整个连接池的最大连接数
			manager.setMaxTotal(500);
			//每个路由的默认最大连接，每个路由实际最大连接数默认为DefaultMaxPerRoute控制，而MaxTotal是控制整个池子最大数
			manager.setDefaultMaxPerRoute(200);
			//在从连接池获取链接时，连接不活跃多长时间后需要进行一次验证，默认为2s
			manager.setValidateAfterInactivity(5 * 1000);

			//默认请求配置
			RequestConfig defaultRequestConfig = RequestConfig.custom()
					//设置连接超时时间,2s
					.setConnectTimeout(5 * 1000)
					//设置等待数据超时时间，5s
					.setSocketTimeout(5 * 1000)
					//设置从连接池获取连接的等待超时时间
					.setConnectionRequestTimeout(5000).build();

			httpClient = HttpClients.custom().setConnectionManager(manager)
					//连接池不是共享模式
					.setConnectionManagerShared(false)
					.evictIdleConnections(60, TimeUnit.SECONDS)
					//定期回收过期连接
					.evictExpiredConnections()
					.setConnectionTimeToLive(60, TimeUnit.SECONDS)
					//设置默认请求配置
					.setDefaultRequestConfig(defaultRequestConfig)
					//连接重用策略，即是否能 keepAlive
					.setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
					//长连接配置，即获取长连接生产多长时间
					.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
					//设置重试次数
					.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)).build();

			//JVM 停止或重启时，关闭连接池释放掉连接
			Runtime.getRuntime().addShutdownHook(new Thread() {

				@Override
				public void run() {
					try {
						httpClient.close();
					} catch (IOException e) {
						LOGGER.error("关闭httpClient出错.", e);
					}
				}
			});
		}
		return httpClient;
	}

	/**
	 * 获取请求参数
	 *
	 * @param parameterMap 参数列表
	 * @return 请求参数
	 */
	private static List<NameValuePair> getParam(Map<String, String> parameterMap) {
		List<NameValuePair> nameValuePairs = new ArrayList<>();
		if (parameterMap != null) {
			Set<String> keySet = parameterMap.keySet();
			for (String key : keySet) {
				nameValuePairs.add(new BasicNameValuePair(key, parameterMap.get(key)));
			}
		}
		return nameValuePairs;
	}

	/**
	 * @param url http 请求链接
	 * @return 请求结果
	 * @throws org.apache.http.HttpException ex
	 */
	public static String doGet(String url) throws HttpException {
		String result = null;
		HttpGet httpGet = new HttpGet(url);
		try {
			CloseableHttpResponse response = getHttpClient().execute(httpGet);
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity());
			} else {
				//请求不为200， 即请求Error 时
				EntityUtils.consume(response.getEntity());
			}
		} catch (IOException e) {
			LOGGER.error("HttpClientUtils-doGet请求错误,请求链接:[{}]", url, e);
			//抛出自定义异常，方便系统处理
			throw new HttpException("HttpClientUtils-doGet-请求错误", e);
		}
		return result;
	}

	/**
	 * Get 请求 with param
	 *
	 * @param url   请求Url
	 * @param param 请求参数
	 * @return 请求结果
	 * @throws org.apache.http.HttpException ex
	 */
	public static String doGet(String url, Map<String, String> param) throws HttpException {
		StringBuilder builder = new StringBuilder(url);
		builder.append("?");
		for (Map.Entry<String, String> entry : param.entrySet()) {
			builder.append(entry.getKey());
			builder.append("=");
			builder.append(entry.getValue());
			builder.append("&");
		}
		return doGet(builder.toString());
	}

	/**
	 * @param url    请求Url
	 * @param params 请求参数
	 * @return 返回结果
	 * @throws org.apache.http.HttpException ex
	 */
	public static String doPost(String url, Map<String, String> params) throws HttpException {
		String result = null;
		HttpPost httpPost = new HttpPost(url);
		try {
			UrlEncodedFormEntity postEntity = new UrlEncodedFormEntity(getParam(params), "UTF-8");
			httpPost.setEntity(postEntity);
			CloseableHttpResponse response = getHttpClient().execute(httpPost);
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity());
			} else {
				//请求不为200， 即请求Error 时
				EntityUtils.consume(response.getEntity());
			}
		} catch (IOException e) {
			LOGGER.error("HttpClientUtils-doPost-请求链接:[{}]", url, e);
			//抛出自定义异常，方便系统处理
			throw new HttpException("HttpClientUtils-doPost-请求错误", e);
		}
		return result;
	}

}